<?php
/*
 * Common functions.
 */
define('ICL_COMMON_FUNCTIONS', true);
/**
 * Calculates relative path for given file.
 * 
 * @param type $file Absolute path to file
 * @return string Relative path
 */
function icl_get_file_relpath($file) {
    $is_https = isset($_SERVER['HTTPS']) && strtolower($_SERVER['HTTPS']) == 'on';
    $http_protocol = $is_https ? 'https' : 'http';
    $base_root = $http_protocol . '://' . $_SERVER['HTTP_HOST'];
    $base_url = $base_root;
    $dir = rtrim(dirname($file), '\/');
    if ($dir) {
        $base_path = $dir;
        $base_url .= $base_path;
        $base_path .= '/';
    } else {
        $base_path = '/';
    }
    $relpath = $base_root
            . str_replace(
                    str_replace('\\', '/', realpath($_SERVER['DOCUMENT_ROOT']))
                    , '', str_replace('\\', '/', dirname($file))
    );
    return $relpath;
}

/**
 * Fix WP's multiarray parsing.
 * 
 * @param type $arg
 * @param type $defaults
 * @return type 
 */
function wpv_parse_args_recursive($arg, $defaults) {
    $temp = false;
    if (isset($arg[0])) {
        $temp = $arg[0];
    } else if (isset($defaults[0])) {
        $temp = $defaults[0];
    }
    $arg = wp_parse_args($arg, $defaults);
    if ($temp) {
        $arg[0] = $temp;
    }
    foreach ($defaults as $default_setting_parent => $default_setting) {
        if (!is_array($default_setting)) {
            if (!isset($arg[$default_setting_parent])) {
                $arg[$default_setting_parent] = $default_setting;
            }
            continue;
        }
        if (!isset($arg[$default_setting_parent])) {
            $arg[$default_setting_parent] = $defaults[$default_setting_parent];
        }
        $arg[$default_setting_parent] = wpv_parse_args_recursive($arg[$default_setting_parent], $defaults[$default_setting_parent]);
    }
    
    return $arg;
}

/**
 * Condition function to evaluate and display given block based on expressions
 * 'args' => arguments for evaluation fields
 * 
 * Supported actions and symbols:
 * 
 * Integer and floating-point numbers
 * Math operators: +, -, *, /
 * Comparison operators: &lt;, &gt;, =, &lt;=, &gt;=, !=
 * Boolean operators: AND, OR, NOT
 * Nested expressions - several levels of brackets
 * Variables defined as shortcode parameters starting with a dollar sign
 * empty() function that checks for blank or non-existing fields
 * 
 * 
 */
function wpv_condition($atts) {
	extract(
        shortcode_atts( array('evaluate' => FALSE), $atts )
    );
    
    global $post;
    
    // if in admin, get the post from the URL
    if(is_admin()) {
        // Get post
        if (isset($_GET['post'])) {
            $post_id = (int) $_GET['post'];
        } else if (isset($_POST['post_ID'])) {
            $post_id = (int) $_POST['post_ID'];
        } else {
            $post_id = 0;
        }
        if ($post_id) {
            $post = get_post($post_id);
        }
    }
    
    global $wplogger;

    $logging_string = "Original expression: ". $evaluate;
    
    $evaluate = apply_filters('wpv-extra-condition-filters', $evaluate);
    
    // evaluate empty() statements for variables
    $empties = preg_match_all("/empty\(\s*\\$(\w+)\s*\)/", $evaluate, $matches);
    
    if($empties && $empties > 0) {
    	for($i = 0; $i < $empties; $i++) {
   		 	$match_var = get_post_meta($post->ID, $atts[$matches[1][$i]], true);
   		 	$is_empty = '1=0';
   		 	
   		 	// mark as empty only nulls and ""  
   		 	if(is_null($match_var) || strlen($match_var) == 0) {
   		 		$is_empty = '1=1';
   		 	}
   		 	
			$evaluate = str_replace($matches[0][$i], $is_empty, $evaluate);
   		 }
    }

    // find variables that are to be used as strings.
	// eg '$f1'
	// will replace $f1 with the actual field value
	$strings_count = preg_match_all('/(\'[\$\w^\']*\')/', $evaluate, $matches);
	if($strings_count && $strings_count > 0) {
	    for($i = 0; $i < $strings_count; $i++) {
	    	$string = $matches[1][$i];
	    	// remove single quotes from string literals to get value only
	    	$string = (strpos($string, '\'') === 0) ? substr($string, 1, strlen($string) - 2) : $string;
	    	if(strpos($string, '$') === 0) {
	    		$variable_name = substr($string, 1); // omit dollar sign
	    		if (isset($atts[$variable_name])) {
					$string = get_post_meta($post->ID, $atts[$variable_name], true);
					$evaluate = str_replace($matches[1][$i], "'" . $string . "'", $evaluate);
				}
	    	}
		}
	}
    
    // find string variables and evaluate
	$strings_count = preg_match_all('/((\$\w+)|(\'[^\']*\'))\s*([\!<>\=]+)\s*((\$\w+)|(\'[^\']*\'))/', $evaluate, $matches);

	// get all string comparisons - with variables and/or literals
	if($strings_count && $strings_count > 0) {
	    for($i = 0; $i < $strings_count; $i++) {
			
	    	// get both sides and sign
	    	$first_string = $matches[1][$i];
	    	$second_string = $matches[5][$i];
	    	$math_sign =  $matches[4][$i];
	    	
	    	// remove single quotes from string literals to get value only
	    	$first_string = (strpos($first_string, '\'') === 0) ? substr($first_string, 1, strlen($first_string) - 2) : $first_string;
	    	$second_string = (strpos($second_string, '\'') === 0) ? substr($second_string, 1, strlen($second_string) - 2) : $second_string; 
	    	
	    	// replace variables with text representation
	    	if(strpos($first_string, '$') === 0) {
	    		$variable_name = substr($first_string, 1); // omit dollar sign
	    		if (isset($atts[$variable_name])) {
					$first_string = get_post_meta($post->ID, $atts[$variable_name], true);
				} else {
					$first_string = '';
				}
	    	}
	    	if(strpos($second_string, '$') === 0) {
	    		$variable_name = substr($second_string, 1);
	    		if (isset($atts[$variable_name])) {
		    		$second_string = get_post_meta($post->ID, $atts[$variable_name], true);
				} else {
					$second_string = '';
				}
	    	}
	    	
	    	// don't do string comparison if variables are numbers 
	    	if(!(is_numeric($first_string) && is_numeric($second_string))) {
	    		// compare string and return true or false
	    		$compared_str_result = wpv_compare_strings($first_string, $second_string, $math_sign);
	    	
		    	if($compared_str_result) {
					$evaluate = str_replace($matches[0][$i], '1=1', $evaluate);
		    	} else {
		    		$evaluate = str_replace($matches[0][$i], '1=0', $evaluate);
		    	}
	    	} else {
				$evaluate = str_replace($matches[1][$i], $first_string, $evaluate);
				$evaluate = str_replace($matches[5][$i], $second_string, $evaluate);
			}
		}
    }

	// find remaining strings that maybe numeric values.
	// This handles 1='1'
	$strings_count = preg_match_all('/(\'[^\']*\')/', $evaluate, $matches);
	if($strings_count && $strings_count > 0) {
	    for($i = 0; $i < $strings_count; $i++) {
	    	$string = $matches[1][$i];
	    	// remove single quotes from string literals to get value only
	    	$string = (strpos($string, '\'') === 0) ? substr($string, 1, strlen($string) - 2) : $string;
			if (is_numeric($string)) {
				$evaluate = str_replace($matches[1][$i], $string, $evaluate);
			}
		}
	}
	

    // find all variable placeholders in expression
    $count = preg_match_all('/\$(\w+)/', $evaluate, $matches);
    
    $logging_string .= "; Variable placeholders: ". var_export($matches[1], true); 
    
    // replace all variables with their values listed as shortcode parameters
    if($count && $count > 0) {
    	// sort array by length desc, fix str_replace incorrect replacement
    	$matches[1] = wpv_sort_matches_by_length($matches[1]);
    	
	    foreach($matches[1] as $match) {
			if (isset($atts[$match])) {
				$meta = get_post_meta($post->ID, $atts[$match], true);
				if (empty($meta)) {
					$meta = "0";
				}
			} else {
				$meta = "0";
			}
	    	$evaluate = str_replace('$'.$match, $meta, $evaluate);
	    }
    }
    
    $logging_string .= "; End evaluated expression: ". $evaluate;
    
    $wplogger->log($logging_string, WPLOG_DEBUG);
    // evaluate the prepared expression using the custom eval script
    $result = wpv_evaluate_expression($evaluate);
    
    // return true, false or error string to the conditional caller
    return $result;
}

function wpv_eval_check_syntax($code) {
    return @eval('return true;' . $code);
}

/**
 * 
 * Sort matches array by length so evaluate longest variable names first
 * 
 * Otherwise the str_replace would break a field named $f11 if there is another field named $f1
 * 
 * @param array $matches all variable names
 */
function wpv_sort_matches_by_length($matches) {
	$length = count($matches);
	for($i = 0; $i < $length; $i++) {
		$max = strlen($matches[$i]);
		$max_index = $i;
		
		// find the longest variable
		for($j = $i+1; $j < $length; $j++) {
			if(strlen($matches[$j]) > $max ) {
				$max = $matches[$j];
				$max_index = $j;
			}
		}
		
		// swap
		$temp = $matches[$i];
		$matches[$i] = $matches[$max_index];
		$matches[$max_index] = $temp;
	}
	
	return 	$matches;
	
}


/**
 * Boolean function for string comparison
 *
 * @param string $first first string to be compared
 * @param string $second second string for comparison
 * 
 * 
 */
function wpv_compare_strings($first, $second, $sign) {
	// get comparison results
	$comparison = strcmp($first, $second);
	
	// verify cases 'less than' and 'less than or equal': <, <=
	if($comparison < 0 && ($sign == '<' || $sign == '<=')) {
		return true;	
	}
	
	// verify cases 'greater than' and 'greater than or equal': >, >=
	if($comparison > 0 && ($sign == '>' || $sign == '>=')) {
		return true;	
	}
	
	// verify equal cases: =, <=, >=
	if($comparison == 0 && ($sign == '=' || $sign == '<=' || $sign == '>=') ) {
		return true;
	}
	
	// verify != case
	if($comparison != 0 && $sign == '!=' ) {
		return true;
	}
	
	// or result is incorrect
	return false;
}

/**
 * 
 * Function that prepares the expression and calls eval()
 * Validates the input for a list of whitechars and handles internal errors if any
 * 
 * @param string $expression the expression to be evaluated 
 */
function wpv_evaluate_expression($expression){
    //Replace AND, OR, ==
    $expression = strtoupper($expression);
    $expression = str_replace("AND", "&&", $expression);
    $expression = str_replace("OR", "||", $expression);
    $expression = str_replace("NOT", "!", $expression);
    $expression = str_replace("=", "==", $expression);
    $expression = str_replace("<==", "<=", $expression);
    $expression = str_replace(">==", ">=", $expression);
    $expression = str_replace("!==", "!=", $expression); // due to the line above
    
    // validate against allowed input characters
	$count = preg_match('/[0-9+-\=\*\/<>&\!\|\s\(\)]+/', $expression, $matches);
	
	// find out if there is full match for the entire expression	
	if($count > 0) {
		if(strlen($matches[0]) == strlen($expression)) {
			 	$valid_eval = wpv_eval_check_syntax("return $expression;");
			 	if($valid_eval) {
			 		return eval("return $expression;");
			 	}
			 	else {
			 		return __("Error while parsing the evaluate expression", 'wpv-views');
			 	}
		}
		else {
			return __("Conditional expression includes illegal characters", 'wpv-views');
		}
	}
	else {
		return __("Correct conditional expression has not been found", 'wpv-views');
	}
	
}

/**
 * class WPV_wpcf_switch_post_from_attr_id
 *
 * This class handles the "id" attribute in a wpv-post-xxxxx shortcode
 * and sets the global $id, $post, and $authordata
 *
 * It also handles types. eg [types field='my-field' id='233']
 *
 * id can be a integer to refer directly to a post
 * id can be $parent to refer to the parent
 * id can be $current_page or refer to the current page
 *
 * id can also refer to a related post type
 * eg. for a stay the related post types could be guest and room
 * [types field='my-field' id='$guest']
 * [types field='my-field' id='$room']
 */

class WPV_wpcf_switch_post_from_attr_id {

    function __construct($atts){
        $this->found = false;
        
        if (isset($atts['id'])) {

            global $post, $authordata, $id, $WPV_wpcf_post_relationship;                                    
            
            $post_id = 0;
            
            if (strpos($atts['id'], '$') === 0) {
                // Handle the parent if the id is $parent
                if ($atts['id'] == '$parent' && isset($post->post_parent)) {
                    $post_id = $post->post_parent;
				} else if ($atts['id'] == '$current_page') {
					if (is_single() || is_page()) {
						global $wp_query;
						
						if (isset($wp_query->posts[0])) {
							$current_post = $wp_query->posts[0];
							$post_id = $current_post->ID;
						}
					}
					
                } else {
                    // See if Views has the variable
                    global $WP_Views;
                    if (isset($WP_Views)) {
                        $post_id = $WP_Views->get_variable($atts['id'] . '_id');
                    }
					if ($post_id == 0) {
						// Try the local storage.
						if (isset($WPV_wpcf_post_relationship[$atts['id'] . '_id'])) {
							$post_id = $WPV_wpcf_post_relationship[$atts['id'] . '_id'];
						}
					}
                }
                
            } else {
                $post_id = intval($atts['id']);
            }
            
            if ($post_id > 0) {
            
                $this->found = true;
    
                // save original post 
                $this->post = isset($post) ? clone $post : null;
                if ($authordata) {
                    $this->authordata = clone $authordata;
                } else {
                    $this->authordata = null;
                }
                $this->id = $id;
                
                // set the global post values
                $id = $post_id;
                $post = get_post($id);
				$authordata = new WP_User($post->post_author);
                
            }   
        }
        
    }
    
    function __destruct(){
        if ($this->found) {
            global $post, $authordata, $id;                                    
            
            // restore the global post values.
            $post = isset($this->post) ? clone $this->post : null;
            if ($this->authordata) {
                $authordata = clone $this->authordata;
            } else {
                $authordata = null;
            }
            $id = $this->id;
        }
        
    }
    
}

// Add a filter on the content so that we can record any related posts.
// These can then be used ine id of Types and Views shortcodes
// eg. for a stay we can have
// [types field='my-field' id="$room"] displays my-field from the related room
// [wpv-post-title id="$room"] display the title of the related room

add_filter('the_content', 'WPV_wpcf_record_post_relationship_belongs', 0, 1);

$WPV_wpcf_post_relationship = Array();

function WPV_wpcf_record_post_relationship_belongs($content) {

	global $post, $WPV_wpcf_post_relationship;
    static $related = array();
	
    if (isset($post) && function_exists('wpcf_pr_get_belongs')) {
        
        if (!isset($related[$post->post_type])) {
            $related[$post->post_type] = wpcf_pr_get_belongs($post->post_type);
        }
        if (is_array($related[$post->post_type])) {
            foreach($related[$post->post_type] as $post_type => $data) {
                $related_id = wpcf_pr_post_get_belongs($post->ID, $post_type);
                if ($related_id) {
                    $WPV_wpcf_post_relationship['$' . $post_type . '_id'] = $related_id;
                }
            }
        }
    }
    

	return $content;
}

/**
 * Form for Enlimbo calls for wpv-control shortcode calls
 
 * @param unknown_type $elements
 */
function wpv_form_control($elements) {
    static $form = NULL;
    require_once 'classes/control_forms.php';
    if (is_null($form)) {
        $form = new Enlimbo_Control_Forms();
    }
    return $form->renderElements($elements);
}

/**
 * Dismiss message.
 * 
 * @param type $message_id
 * @param string $message
 * @param type $class 
 */
function wpv_add_dismiss_message($message_id, $message, $clear_dismissed = false, $class = 'updated') {
    $dismissed_messages = get_option('wpv-dismissed-messages', array());
	if ($clear_dismissed) {
		if (isset($dismissed_messages[$message_id])) {
			unset($dismissed_messages[$message_id]);
	        update_option('wpv-dismissed-messages', $dismissed_messages);
		}
	}
    if (!array_key_exists($message_id, $dismissed_messages)) {
        $message = $message . '<div style="float:right; margin:-15px 0 0 15px;"><a onclick="jQuery(this).parent().parent().fadeOut();jQuery.get(\''
                . admin_url('admin-ajax.php?action=wpv_dismiss_message&amp;message_id='
                        . $message_id . '&amp;_wpnonce='
                        . wp_create_nonce('dismiss_message')) . '\');return false;"'
                . 'class="button-secondary" href="javascript:void(0);">'
                . __("Don't show this message again", 'wpv-views') . '</a></div>';
        wpv_admin_message_store($message_id, $message, false);
    }
}

add_action('wp_ajax_wpv_dismiss_message', 'wpv_dismiss_message_ajax');

/**
 * Dismiss message AJAX. 
 */
function wpv_dismiss_message_ajax() {
    if (isset($_GET['message_id']) && isset($_GET['_wpnonce'])
            && wp_verify_nonce($_GET['_wpnonce'], 'dismiss_message')) {
        $dismissed_messages = get_option('wpv-dismissed-messages', array());
        $dismissed_messages[strval($_GET['message_id'])] = 1;
        update_option('wpv-dismissed-messages', $dismissed_messages);
    }
    die('ajax');
}

// disable the admin messages for now. They are causing problems.
//add_action('admin_head', 'wpv_show_admin_messages');

/**
 * Shows stored admin messages. 
 */
function wpv_show_admin_messages() {
    $messages = get_option('wpv-messages', array());
    $dismissed_messages = get_option('wpv-dismissed-messages', array());
    foreach ($messages as $message_id => $message) {
        if (array_key_exists($message_id, $dismissed_messages)) {
            unset($messages[$message_id]);
            continue;
        }
		// update the nonce
		$text = $message['message'];
	    $nonce = preg_match_all("/_wpnonce=[^']+/", $text, $matches);
		
		if ($nonce) {
			$text = str_replace($matches[0][0], '_wpnonce=' . wp_create_nonce('dismiss_message'), $text);
		}
		
        wpv_admin_message($message_id, $text, $message['class']);
        if ($show_once) {
            unset($messages[$message_id]);
        }
    }
    update_option('wpv-messages', $messages);
}

/**
 * Stores admin messages.
 * 
 * @param type $message_id
 * @param type $message
 * @param type $show_once
 * @param type $class 
 */
function wpv_admin_message_store($message_id, $message, $show_once = true,
        $class = 'updated') {
    $messages = get_option('wpv-messages', array());
    $messages[strval($message_id)] = array(
        'message' => strval($message),
        'class' => strval($class),
        'show_once' => $show_once,
    );
    update_option('wpv-messages', $messages);
}

/**
 * Shows admin message.
 * 
 * @param type $message_id
 * @param type $message
 * @param type $class 
 */
function wpv_admin_message($message_id, $message, $class = 'updated') {
    if (apply_filters('wpv-show-message', true, $message_id)) {
		add_action('admin_notices',
                create_function('$a=1, $message_id=\'' . strval($message_id)
                        . '\', $class=\'' . strval($class)
                        . '\', $message=\''
                        . htmlentities(strval($message), ENT_QUOTES) . '\'',
                        '$screen = get_current_screen(); if (!$screen->is_network) echo "<div class=\"message $class\" id=\"wpv-message-$message_id\"><p>" . html_entity_decode($message, ENT_QUOTES) . "</p></div>";'));
	}
}
